package naito_rescue.communication.team.generator;

import java.util.*;
import sample.*;
import rescuecore2.messages.*;
import rescuecore2.worldmodel.*;
import rescuecore2.Constants;
import rescuecore2.config.*;
import rescuecore2.standard.components.*;
import rescuecore2.standard.entities.*;
import rescuecore2.standard.messages.*;
import rescuecore2.misc.geometry.*;

import naito_rescue.*;
import naito_rescue.agent.*;
import naito_rescue.communication.team.*;
import naito_rescue.communication.team.generator.*;
import naito_rescue.communication.channel.*;
import naito_rescue.object.*;
import naito_rescue.util.comm.team.*;
import naito_rescue.message.*;
import naito_rescue.message.manager.*;
import naito_rescue.util.comm.team.*;


// BasicCommunicationTeamのリストを返す
// BasicCommunicationTeamはこいつの内部クラスにしちゃう予定
// (実装を外部に知らしめることなく，インタフェースに基づく設計が可能になるため)
public class BasicCommunicationTeamGenerator implements ICommunicationTeamsGenerator
{
	
	private Config config;
	private List<StandardEntity> allPlatoons;
	private List<RadioChannel>   allRadioChannels;
	private MyLogger             logger;
	private int                  teamNum = 0;
	
	public BasicCommunicationTeamGenerator( List<StandardEntity> allPlatoons,
	                                        List<RadioChannel> allRadioChannels,
	                                        Config config,
	                                        MyLogger logger ){
		//assert allPlatoons != null
		this.allPlatoons = CommunicationTeamUtil.createAgentCluster(allPlatoons);
		//assert allRadioChannels != null && allRadioChannels.size() >= 3
		this.allRadioChannels = allRadioChannels;
		this.config = config;
		this.logger = logger;
	}
	
	@Override
	public List<ICommunicationTeam> generateTeams(){
		List<ICommunicationTeam> teams = new ArrayList<ICommunicationTeam>();
		List<StandardEntity> copyPlatoons = new ArrayList<StandardEntity>();
		List<RadioChannel> copyChannels = new ArrayList<RadioChannel>();
		
		//logger.info("BasicCommunicationTeamGenerator#generateTeams();");
		for(StandardEntity entity : this.allPlatoons){
			copyPlatoons.add(entity);
		}
		for(RadioChannel ch : this.allRadioChannels){
			copyChannels.add(ch);
		}
		// チャネルの数よりプラトーンの数の方が大きかった場合は? -> 統一的に扱えないの?
		int sizeofMember = copyPlatoons.size() / copyChannels.size();
		//
		while(!copyPlatoons.isEmpty() && !copyChannels.isEmpty()){
			if(sizeofMember > copyPlatoons.size()){
				sizeofMember = copyPlatoons.size();
			}
			//logger.debug("Create BasicCommunication Team...");
			List<StandardEntity> subMembers = new ArrayList<StandardEntity>();
			subMembers.addAll(copyPlatoons.subList(0, sizeofMember));
			RadioChannel channel = copyChannels.remove(0);
			List<AbstractChannel> channelsToOtherTeam = CommunicationTeamUtil.getChannelsToOtherTeam(this.allRadioChannels, channel);
			
			//logger.trace("sizeofMember            = " + sizeofMember);
			//logger.trace("Members are             = " + subMembers);
			//logger.trace("Team Channel is         = " + channel);
			//logger.trace("Other team channels are = " + channelsToOtherTeam);
			
			//logger.info(" ");
			ICommunicationTeam team = new BasicCommunicationTeam(++teamNum, subMembers, channel, channelsToOtherTeam, this.config);
			teams.add(team);
			//logger.info(" ");
			
			//logger.debug("Creating result = " + team);
			copyPlatoons.subList(0, sizeofMember).clear(); // 今チームに入れたメンバをリストから削除
			//logger.debug("Remaind members are = " + copyPlatoons);
		}
		// 余ったら?
		if(!copyPlatoons.isEmpty()){
			// 余ったらそいつらだけNoCommuってのはどうよ
			// Generatorの扱いが面倒くさいことになりそう...
		}
		
		//logger.info("BasicCommunicationTeamGenerator#generateTeams() end;");
		return teams;
	}
	
	
	private class BasicCommunicationTeam implements ICommunicationTeam
	{
		private static final int                      DESIRED_BANDWIDTH_PER_AGENT = 50;
		private              int                      teamNum;
		private              List<StandardEntity>     members;
		private              AbstractChannel          myTeamChannel;
		private              List<AbstractChannel>    otherTeamChannels;
		private              boolean                  shouldRotate = false;
		private              int                      numOfRotationMember;
		private              int                      ignoreUntil = 3; //defaultは大抵3
		private              int                      bandwidthPerAgent;
		private              Config                   config;
		
		public BasicCommunicationTeam( int teamNum,
										   List<StandardEntity> members,
		                               RadioChannel myTeamChannel,
		                               List<AbstractChannel> otherTeamChannels,
		                               Config config ){

			this.teamNum = teamNum;
			this.members = members;
			this.myTeamChannel = myTeamChannel;
			this.otherTeamChannels = otherTeamChannels;
			this.config = config;
			this.ignoreUntil = config.getIntValue(kernel.KernelConstants.IGNORE_AGENT_COMMANDS_KEY);
			
			//logger.info("BasicCommunicationTeam#<Constructor>();");
			int bandwidth = this.myTeamChannel.getBandwidth();
			int channelUsers = this.members.size() + this.otherTeamChannels.size();
			int bwPerAgent = bandwidth / channelUsers;
			
			//logger.debug("This team's size is     = " + members.size());
			//logger.debug("Team channel is         = " + myTeamChannel);
			//logger.debug("Other team channels are = " + otherTeamChannels);
			//logger.debug("bandwidth / member's size = " + bwPerAgent);
			//logger.debug("DESIRED_BANDWIDTH_PER_AGENT = " + DESIRED_BANDWIDTH_PER_AGENT);
			
			if(bwPerAgent < DESIRED_BANDWIDTH_PER_AGENT){
				this.shouldRotate = true;
				this.numOfRotationMember = (bandwidth / DESIRED_BANDWIDTH_PER_AGENT) - this.otherTeamChannels.size();
				this.bandwidthPerAgent = DESIRED_BANDWIDTH_PER_AGENT;
				//logger.debug("This team should rotate member.");
				//logger.debug("numOfRotationMember = " + numOfRotationMember);
				//logger.debug("bandwidthPerAgent = " + bandwidthPerAgent + " (DESIRED_BANDWIDTH_PER_AGENT = " + DESIRED_BANDWIDTH_PER_AGENT + ")");
			}else{
				this.bandwidthPerAgent = bwPerAgent;
				//logger.debug("This team should not rotate member.");
			}
			//logger.info("BasicCommunicationTeam#<Constructor>() end;");
		}
		@Override
		public List<AbstractChannel> getChannelToFB(){
			return Arrays.asList(myTeamChannel);
		}
		@Override
		public List<AbstractChannel> getChannelToPF(){
			return Arrays.asList(myTeamChannel);
		}
		@Override
		public List<AbstractChannel> getChannelToAT(){
			return Arrays.asList(myTeamChannel);
		}
		@Override
		public AbstractChannel getMyChannel(){
			return myTeamChannel;
		}
		@Override
		public List<StandardEntity> getMembers(){
			return this.members;
		}
		@Override
		public List<AbstractChannel> getChannelsToOtherTeam(){
			return this.otherTeamChannels;
		}
		@Override
		public boolean isMyTurn(StandardEntity sender, int timestep){
			//logger.info("BasicCommunicationTeam#isMyTurn();");
			if(!this.members.contains(sender)){
				//logger.debug("This team is not contains sender.");
				//logger.debug("members = " + this.members);
				//logger.debug("sender  = " + sender);
				//logger.info("BasicCommunicationTeam#isMyTurn() end;");
				return false;
			}
			boolean result;
			if(shouldRotate){
				//logger.debug("timestep = " + timestep);
				List<StandardEntity> currentTurn
					= CommunicationTeamUtil.getAgentsAfterRotation
						(this.members, timestep, this.ignoreUntil, this.numOfRotationMember);
				//logger.debug("Members who can send messages in this timestep are = " + currentTurn);
				//logger.debug("(" + currentTurn.size() + " users)");
				if(currentTurn.contains(sender)){
					//logger.debug("return true;");
					result = true;
				}else{
					//logger.debug("return true;");
					result = false;
				}
			}else{
				result = true;
			}
			//logger.info("BasicCommunicationTeam#isMyTurn() end;");
			return result;
		}
		@Override
		public int getBandwidthPerAgent(){
			return bandwidthPerAgent;
		}
		@Override
		public String toString(){
			StringBuffer sb = new StringBuffer();
			sb.append("[BasicCommunicationTeam(" + teamNum + "):");
			sb.append("FBChannel=" + myTeamChannel.getChannelNum() + ",");
			sb.append("PFChannel=" + myTeamChannel.getChannelNum() + ",");
			sb.append("ATChannel=" + myTeamChannel.getChannelNum() + ",");
			sb.append("numOfMember=" + this.members.size() + ",");
			sb.append("rotation=" + this.shouldRotate + ",");
			sb.append("bandwidthPerAgent = " + bandwidthPerAgent + "]");
			return sb.toString();
		}
	}
}
